package main

import (
	"errors"
	"fmt"
)

//参数类型简写
func intSum(a, b, c int) int {
	return a + b + c
}

//可变参数
func intSum2(a ...int) (sum int, slice []int) {
	fmt.Printf("可变参数是：%v \n", a)
	sum = 0
	for _, val := range a {
		sum += val
	}
	slice = a
	return
}

// 变量作用域
var global1 = "global var" // 全局变量

func globalVal() {
	// global1 := 188
	fmt.Printf("Global val: %v \n", global1)
}

//定义函数类型
type numint func(int, int) int

func add(a, b int) int {
	return a + b
}
func sub(a, b int) int {
	return a - b
}

var numa numint = add

// 高阶函数
// 函数座位参数
func calc(a, b int, fu func(int, int) int) int {
	return fu(a, b)
}

//函数作为返回值
//返回值最好带时 error 类型返回值，因为必需要函数返回值
func calc2(x, y int, s string) (func(int, int) int, error) {
	switch s {
	case "+":
		return add, nil // 返回值是函数，因此不能返回函数调用 add(x, y)，返回的函数调用结果是函数的返回值，不是定义的返回值函数类型；因此参数x，y无意义，不用传
	case "-":
		return sub, nil
	default:
		err := errors.New("无法识别的操作符")
		return nil, err
	}
}
func calc3(s string) func(int, int) int {
	switch s {
	case "+":
		return add
	case "-":
		return sub
	default:
		fmt.Println("无法识别的操作符 \n")
		return nil
	}
}

//匿名函数和闭包
//匿名函数
//匿名函数因为没有函数名，所以没办法像普通函数那样调用，所以匿名函数需要保存到某个变量或者作为立即执行函数:
func noname(x, y int) int {
	cheng := func(a, b int) int {
		return a * b
	}(x, y)

	fmt.Printf("%T %v \n", cheng, cheng)

	return func(c, d int) int {
		return c * d
	}(x, y)

}

//闭包
//闭包指的是一个函数和与其相关的引用环境组合而成的实体。简单来说，闭包=函数+引用环境。
var x int

func adder() func(y int) int {
	// var x int
	return func(y int) int {
		x += y
		return x
	}
}

/*
作业
你有50枚金币，需要分配给以下几个人：Matthew,Sarah,Augustus,Heidi,Emilie,Peter,Giana,Adriano,Aaron,Elizabeth。
分配规则如下：
a. 名字中每包含1个'e'或'E'分1枚金币
b. 名字中每包含1个'i'或'I'分2枚金币
c. 名字中每包含1个'o'或'O'分3枚金币
d: 名字中每包含1个'u'或'U'分4枚金币
写一个程序，计算每个用户分到多少金币，以及最后剩余多少金币？
程序结构如下，请实现 ‘dispatchCoin’ 函数
*/
var (
	coins = 50
	users = []string{
		"Matthew", "Sarah", "Augustus", "Heidi", "Emilie", "Peter", "Giana", "Adriano", "Aaron", "Elizabeth",
	}
	distribution = make(map[string]int, len(users))
)

func dispatchCoin() int {
	//遍历users
	for _, uname := range users {
		distribution[uname] = 0
		for _, val := range uname {
			// 判断user得几个金币
			switch val {
			case 'e', 'E':
				distribution[uname] += 1
			case 'i', 'I':
				distribution[uname] += 2
			case 'o', 'O':
				distribution[uname] += 3
			case 'u', 'U':
				distribution[uname] += 4
			default:
			}
		}
		fmt.Printf("%s 获得金币数：%d \n", uname, distribution[uname])
		coins -= distribution[uname]
	}
	//计算剩余金币数
	return coins
}

func main() {
	fmt.Println(intSum(11, 15, 18))
	fmt.Println(intSum2(11, 15, 18))
	globalVal()

	fmt.Printf("函数作为参数的函数：%T  %v \n", numa, numa(2, 5))
	// fmt.Printf("%T %v \n", calc(11, 15, add), calc(11, 15, add))
	res, err := calc2(11, 15, "++")
	if err == nil {
		fmt.Printf("函数返回值函数1：%T %v \n", res, res(11, 15))
	} else {
		fmt.Printf("函数返回值函数1：%T %v \n", err, err)
	}

	res2 := calc3("+")
	fmt.Printf("函数返回值函数2：%T %v \n", res2, res2(11, 15))

	nonameval := noname(11, 15)
	fmt.Printf("匿名函数：%T %v \n", nonameval, nonameval)

	// 变量f是一个函数并且它引用了其外部作用域中的x变量，此时f就是一个闭包。 在f的生命周期内，变量x也一直有效。
	bibao := adder()
	fmt.Printf("闭包函数1：%T %v \n", bibao, bibao(15))
	defer fmt.Printf("闭包函数2：%T %v \n", bibao, bibao(15)) // 多defer按先入后出顺序执行，而defer语句执行的时机就在返回值赋值操作后，RET指令执行前。因此会先计算后执行plf语法
	defer fmt.Printf("闭包函数3：%T %v \n", bibao, bibao(15))

	// 作业
	left := dispatchCoin()
	fmt.Println("剩下金币数：", left)

}
